/*
 * Copyright (C) 2009 Ionut Dediu <deionut@yahoo.com>
 *
 * Licensed under the GNU General Public License Version 2
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

// CommFilter.cpp

#include <eikenv.h>				// CEikonEnv
#include <aknnotewrappers.h>

#include "CommFilter.h"
#include "SmartCamAppUi.h"
#include "SmartCamConst.h"

CCommFilter* CCommFilter::NewL()
	{
	CCommFilter* self = NewLC();
	CleanupStack::Pop();
	return self;
	}

CCommFilter* CCommFilter::NewLC()
	{
	CCommFilter* self = new (ELeave) CCommFilter();
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
	}

CCommFilter::CCommFilter():
	CActive(CActive::EPriorityStandard),
	iStreamMgr(NULL),
	iBtDevAddr(),
	iBtDiscoverer(NULL),
	iTimeWrap(NULL),
	iSendBuffer1(NULL),
	iSendBuffer2(NULL),
	iCrtSendBuffer(NULL),
	iCommState(EDisconnected)
	{
	CActiveScheduler::Add(this);
	}

void CCommFilter::ConstructL()
	{
	iTimeWrap = CTimeWrap::NewL(*this);
	iSendBuffer1 = HBufC8::NewL(KDefaultSendBufferSize);
	iSendBuffer1->Des().Zero();
	iSendBuffer2 = HBufC8::NewL(KDefaultSendBufferSize);
	iSendBuffer2->Des().Zero();
	}

CCommFilter::~CCommFilter()
	{
	if(IsActive())
		{
		Cancel();
		}
	if(iTimeWrap != NULL)
		{
		delete iTimeWrap;
		iTimeWrap = NULL;
		}
	if(iSendBuffer1 != NULL)
		{
		delete iSendBuffer1;
		iSendBuffer1 = NULL;
		}
	if(iSendBuffer2 != NULL)
		{
		delete iSendBuffer2;
		iSendBuffer2 = NULL;
		}
	iCrtSendBuffer = NULL;
	if(iBtDiscoverer != NULL)
		{
		delete iBtDiscoverer;
		iBtDiscoverer = NULL;
		}
	}

void CCommFilter::DoCancel()
	{
	// Cancel any pending requests
	iClientSocket.CancelAll();
	}

void CCommFilter::StartBtDiscovery()
	{
	iCommState = EDiscovering;
	if(iBtDiscoverer != NULL)
		{
		delete iBtDiscoverer;
		iBtDiscoverer = NULL;
		}
	iBtDiscoverer = CBtDiscoverer::NewL(*this);
	TDes& btDevAddrStr = ((CSmartCamAppUi*)iStreamMgr)->GetBtServerAddr();
	iBtDevAddr.SetReadable(btDevAddrStr);
	iBtDiscoverer->FindRemoteService(iBtDevAddr);
	}

// from MSmartDiscoveryObserver:

void CCommFilter::RemoteDeviceSelected(TInt aErrorCode)
	{
	}

void CCommFilter::RemoteServiceFound(TInt aErrorCode)
	{
	if(aErrorCode == KErrNone)
		{
		ConnectToBtServerL();
		}
	else
		{
		TBuf<60> errMsg;
		errMsg.Format(_L("Bluetooth discovery error %d"), aErrorCode);
		iStreamMgr->OpenComplete(this, aErrorCode, errMsg);
		}
	}

void CCommFilter::ConnectToInetServerNameL(const TDesC& aInetServerName)
    {
    TInetAddr addr;
    if(addr.Input(aInetServerName) == KErrNone)
        {
        // server name is already a valid ip address
        ConnectToInetServerAddrL(addr.Address());
        }
    else
        {
        // Initiate DNS
        User::LeaveIfError(iResolver.Open(iSocketServer, KAfInet, KProtocolInetTcp));
        // Start up DNS lookup time out timer
        iTimeWrap->After(KDnsLookupTimeout);
        // DNS request for name resolution
        iCommState = EDiscovering;
        iResolver.GetByName(aInetServerName, iNameEntry, iStatus);
        SetActive();
        }
    }

void CCommFilter::ConnectToInetServerAddrL(TUint32 aInetServerAddr)
    {
    iInetServerAddr.SetAddress(aInetServerAddr);
    iInetServerAddr.SetFamily(KAfInet);
    iInetServerAddr.SetPort(((CSmartCamAppUi*)iStreamMgr)->GetInetPort());

    // Open a TCP socket
    User::LeaveIfError(
    	iClientSocket.Open(iSocketServer, KAfInet, KSockStream, KProtocolInetTcp));

    // Start up connection time out timer
    iTimeWrap->After(KConnectionTimeout);
    // Initiate socket connection
    iCommState = EConnecting;
    iClientSocket.Connect(iInetServerAddr, iStatus);
    SetActive();
    }

void CCommFilter::ConnectToBtServerL()
	{
	iCommState = EConnecting;
	iBtServerAddr.SetBTAddr(iBtDevAddr);
	iBtServerAddr.SetPort(iBtDiscoverer->Port());

	User::LeaveIfError(iClientSocket.Open(iSocketServer, KServerTransportName));

	iClientSocket.Connect(iBtServerAddr, iStatus);
	SetActive();
	}

void CCommFilter::RunL()
	{
	if(iStatus.Int() != KErrNone) // ERROR
		{
		TBuf<50> errMsg;
		switch(iCommState)
			{
			case EDiscovering:
		        iCommState = EDisconnected;
				if(iStatus.Int() == KErrCancel) // Timer expired and cancelled DNS GetByName() request
					{
			        iResolver.Close();
			        iSocketServer.Close();
			        iStreamMgr->OpenComplete(this, KErrHostUnreach, KStrResolveNameTimedOut);
					}
				else // DNS server responded with error
					{
					iTimeWrap->Cancel();
					iSocketServer.Close();
					errMsg.Format(KStrFmtCouldNotResolveName, iStatus.Int());
					iStreamMgr->OpenComplete(this, iStatus.Int(), errMsg);
					}
				break;
			case EConnecting:
				iCommState = EDisconnected;
				if(iStatus.Int() == KErrCancel) // Timer expired and cancelled Connect() request
					{
					iClientSocket.Close();
					iSocketServer.Close();
					iStreamMgr->OpenComplete(this, KErrHostUnreach, KStrConnectionTimedOut);
					}
				else // remote server responded with error on Connect() request
					{
					iTimeWrap->Cancel();
					iClientSocket.Close();
					iSocketServer.Close();
					errMsg.Format(KStrFmtCouldNotConnect, iStatus.Int());
					iStreamMgr->OpenComplete(this, iStatus.Int(), errMsg);
					}
				break;
			case EConnected:
			case ESendingPacket:
			case ESentPacket:
				errMsg.Format(_L("Disconnected %d"), iStatus.Int());
				iStreamMgr->OnFilterError(this, iStatus.Int(), errMsg);
				break;
			}
		}
	else // SUCCESS
		{
		switch(iCommState)
			{
			case EDiscovering:
	            iTimeWrap->Cancel();
	            iResolver.Close();
	            // DNS look up successful
	        	iNameRecord = iNameEntry();
	        	ConnectToInetServerAddrL(TInetAddr::Cast(iNameRecord.iAddr).Address());
				break;
			case EConnecting:
				iCommState = EConnected;
				iStreamMgr->OpenComplete(this, KErrNone, KNullDesC);
				break;
			// Finished sending the crt packet, send the second (cached) one (if any)
			case ESendingPacket:
				iCommState = ESentPacket;
				iCrtSendBuffer->Des().Zero();
				iCrtSendBuffer = NULL;

				if((iSendBuffer1->Size() != 0) || (iSendBuffer2->Size() != 0))
					{
					if(iSendBuffer1->Size() != 0)
						iCrtSendBuffer = iSendBuffer1;
					else if(iSendBuffer2->Size() != 0)
						iCrtSendBuffer = iSendBuffer2;

					iCommState = ESendingPacket;
					iClientSocket.Send(*iCrtSendBuffer, 0, iStatus);
					SetActive();
					}
				break;
			}
		}
	}

// From MSmartFilter
void CCommFilter::Open(MStreamManager* aMgr)
	{
	iStreamMgr = aMgr;
	User::LeaveIfError(iSocketServer.Connect());
	TConnType connType = ((CSmartCamAppUi*)iStreamMgr)->GetConnectionType();
	switch(connType)
		{
		case EConnectionUsb:
			break;
		case EConnectionBluetooth:
			StartBtDiscovery();
			break;
		case EConnectionInet:
			ConnectToInetServerNameL(((CSmartCamAppUi*)iStreamMgr)->GetInetServer());
			break;
		}
	}

void CCommFilter::Close()
	{
	if(IsActive())
		{
		Cancel();
		}
	iTimeWrap->Cancel();
	iCommState = EDisconnected;

	iSendBuffer1->Des().Zero();
	iSendBuffer2->Des().Zero();
	iCrtSendBuffer = NULL;

	TRequestStatus status;
    iClientSocket.Shutdown(RSocket::EImmediate, status);
    User::WaitForRequest(status);
	iClientSocket.Close();
	iSocketServer.Close();
	}

void CCommFilter::TimeElapsed()
	{
	TConnType connType = ((CSmartCamAppUi*)iStreamMgr)->GetConnectionType();
    // DNS lookup did not complete in time.
	if(iCommState == EDiscovering && connType == EConnectionInet)
		{
        iResolver.Cancel(); // handle in RunL(): iCommState = EDiscovering, iStatus = KErrCancel
		}
    // Connection did not complete in time; handle in RunL(): iCommState = EConnecting, iStatus = KErrCancel
	else if(iCommState == EConnecting && connType == EConnectionInet)
		{
        iClientSocket.CancelAll();
		}
	}

TBool CCommFilter::CanAcceptMore()
	{
	return (iSendBuffer1->Size() == 0) || (iSendBuffer2->Size() == 0);
	}

void CCommFilter::SendPacket(TSmartCamPacketType aPacketType, const TDesC8& aPacketData)
	{
	TInt dataSize = aPacketData.Size();
	HBufC8* freeBuffer = NULL;
	if(iSendBuffer1->Size() == 0)
		{
		AssureSizeL(iSendBuffer1, 4 + dataSize);
		freeBuffer = iSendBuffer1;
		}
	else if(iSendBuffer2->Size() == 0)
		{
		AssureSizeL(iSendBuffer2, 4 + dataSize);
		freeBuffer = iSendBuffer2;
		}
	else
		return;

	TPtr8 freeBufferPtr = freeBuffer->Des();
	freeBufferPtr.Append(aPacketType);
	freeBufferPtr.Append((dataSize >> 16) & 0xff);
	freeBufferPtr.Append((dataSize >> 8) & 0xff);
	freeBufferPtr.Append((dataSize >> 0) & 0xff);
	freeBufferPtr.Append(aPacketData);
	if(!IsActive())
		{
		iCrtSendBuffer = freeBuffer;
		iCommState = ESendingPacket;
		iClientSocket.Send(*iCrtSendBuffer, 0, iStatus);
		SetActive();
		}
	}

void CCommFilter::AssureSizeL(HBufC8*& aBuffer, TInt aSize)
	{
	TInt size = aBuffer->Des().MaxSize();
	if(size < aSize)
		{
		while(size < aSize)
			size *= 2;

		HBufC8* newBuf = HBufC8::NewL(size);
		delete aBuffer;
		aBuffer = newBuf;
		}
	}

